/*
** File: dyn_page.c
** Desc: A dynamic buffer, and functions to manipulate it ...
** Auth: Cian Synnott <pooka@redbrick.dcu.ie>
** Date: Tue Nov 10 21:58:17 GMT 1998
**
** $Id: dyn_page.c,v 1.3 2003-08-11 01:21:45 c-hey Exp $
*/

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "dyn_page.h"

/* Allocate a page structure, giving it the width at which to wrap lines 
** appended to it. */
dyn_page *dyn_pageAlloc(int width) {
	dyn_page *p;

	/* Allocate memory */
	if (!(p = (dyn_page *) malloc(sizeof(dyn_page)))) {
		perror("Error allocating page");
		exit(1);
	}

	/* Setup values */
	p->buff = NULL;
	p->rows = 0;
	p->cols = width;
	
	return p;
}

/* Free an entire page structure. */
void dyn_pageFree(dyn_page *p) {
	int i;

	/* Free all the separate lines */
	for (i = 0; i < p->rows; i++) {
		free(p->buff[i]);
		p->buff[i] = NULL;	/* Ok, so I'm paranoid ... */
	}

	/* Free the 'page pointer' */
	free(p->buff);
	p->buff = NULL;

	/* Free the structure memory */
	free(p);

	return;
}
	
/* How many lines, or what width, is the page ... 
** Should be macros, but I like to keep my functions together :o) */
int dyn_pageLines(dyn_page *page) {
	return page->rows;
}

int dyn_pageWidth(dyn_page *page) {
	return page->cols;
}

/* Append a line to the page, wrapping as necessary */
int dyn_pageAppend(dyn_page *p, char *line) {
	char *line_ptr;
	int   last_space = 0;
	int   len = strlen(line);

	/* Firstly, if now is the time to allocate more pointers to lines, 
	** do it (remember to set all these pointers to NULL ... */
	if (!(p->rows % 10)) {
		if (!(p->buff = (char **) realloc(p->buff, (p->rows + 10) * sizeof(char *)))) 
			return 0;
	}
	
	/* We're adding a line, so increment p->rows. Would be nicer to do 
	** this later, but control leaves this function suddenly quite a lot */
	p->rows++;

	
	/* If there's no word-wrapping specified (p->cols == 0), or if the line
	** is less than p->cols anyhows, just add the line */
	if (!p->cols || len < p->cols) {
		
		/* Allocate the space for the line ... */
		if (!(p->buff[p->rows - 1] = (char *) calloc(len + 1, sizeof(char))))
			return 0;

		/* Copy in the line ... */
		strncpy(p->buff[p->rows - 1], line, len);

		/* Success ... */
		return 1;
	}

	/* Otherwise, search for the last space before the end of the wrapping 
	** line (p->cols) and add the line up to that, then call ourselves for
	** the rest of the line. Huzzah for recursion. */
	for (line_ptr = line; *line_ptr; line_ptr++) {
		
		/* If we're at a column boundry (setting len == difference between 
		** line & line_ptr ... */
		if ((len = line_ptr - line) + 1 == p->cols) {
			
			/* If there was a last space recorded, set len equal to it */
			if (last_space) len = last_space;

			/* Allocate the space for the line ... */
			if (!(p->buff[p->rows - 1] = (char *) calloc(len + 1, sizeof(char))))
				return 0;
			
			/* Copy the stated length into it ... */
			strncpy(p->buff[p->rows - 1], line, len);

			/* Set line equal to it's new value & skip any whitespace
			** at the start of it ... */
			line += len;

			while (*line && *line == ' ') line++;

			/* Now call dyn_pageAppend() to add the rest of it, 
			** propogating results back up the recursive calls ... */
			return dyn_pageAppend(p, line);
		}	

		/* Otherwise control reaches here ... */
		if (*line_ptr == ' ') last_space = (line_ptr - line);
	
	} /* End while (*line_ptr) */

	/* Control never reaches here ... */
	return 0;
}

/* Extract a line by index */
char *dyn_pageLineAt(dyn_page *p, int line) {
	/* Invalid reference ... */
	if (line >= p->rows || line < 0) return NULL;

	/* Otherwise return the line asked for */
	return p->buff[line];
}

/* Modify a line by index */
int dyn_pageLineMod(dyn_page *p, char *line, int lineno)
{
	/* Invalid reference ... */
	if (lineno >= p->rows || lineno < 0) return 0;

	/* free the existing line */
	if((p->buff[lineno]=(char *)realloc(p->buff[lineno],strlen(line) + 1)) == NULL) 
		return 0;

	/* modify the line */
	strcpy(p->buff[lineno], line);

	return 1;

}

/* Step along the page, returning the next line on subsequent calls that are
** passed NULL. Standard iterator ... */
char *dyn_pageRead(dyn_page *p) {
	static dyn_page *on_page = NULL;
	static int line = 0;

	if (p) {
		on_page = p;
		line = 0;

		if (!on_page->rows) 
			return NULL;

		return on_page->buff[line++];
	}
	
	/* If we get this far, if there's no current page, error */
	if (!on_page) 
		return NULL;

	/* Otherwise return line asked for */
	else {
		if (line >= on_page->rows) return NULL;
		
		return on_page->buff[line++];
	}

	/* Control never reaches here */
	return NULL;
}
